#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <regex.h>
#include <unistd.h>
#include <wait.h>


// replaces the matches with the compiled regex in the string with the substrings in subs. If there are more matches than substring, the excess matches are replaced with nothing.
char* regex_sub_v(regex_t regex, char* string, char** subs){
	regmatch_t match[1];
	if (regexec(&regex, string, 1, match, 0) == 0){
		char* newstring;
		char* rec_string;
		if ( subs[0] != NULL ){
			newstring=malloc(sizeof(char)*(strlen(subs[0]) + strlen(string) + match[0].rm_eo - match[0].rm_so));
			memset(newstring, '\0', sizeof(char)*strlen(newstring));
			strncpy(newstring, string, match[0].rm_so);
			strcat(newstring, subs[0]);
			rec_string = regex_sub_v(regex,string+match[0].rm_eo, subs+1);
		}
		else {
			newstring=malloc(sizeof(char)*(strlen(string)+match[0].rm_eo - match[0].rm_so));
			memset(newstring, '\0', sizeof(char)*strlen(newstring));
			strncpy(newstring, string, match[0].rm_so);
			rec_string = regex_sub_v(regex,string+match[0].rm_eo, NULL);
		}

		char* ret_string = malloc (sizeof(char)*(strlen(newstring)+strlen(rec_string)));
		memset(ret_string, '\0', strlen(ret_string));
		strcpy(ret_string, newstring);
		strcat(ret_string, rec_string);
		free(newstring);
		return ret_string;
	}
	return string;
}

int main(int argc, char** argv){

	//Initialization and reading arguments
	
	int do_fork = 1;

	while(1){

		if (argv[1] == NULL) {
			fprintf(stderr, "No file specified\n");
			return 1;
		}
		
		if (strncmp(argv[1], "-h", 2) == 0){
			printf("Usage:\ndot-desktop [OPTIONS] application [application args]\n\nOptions:\n-h\t\tPrint help.\n--no-fork\tDon't fork launched process to the background.\n\nSet the DEFAULT_TERM environment variable to choose which terminal emulator to open apps it, defaults to 'xterm -e' if unset.\n");
			return 0;
		}
		
		if (strncmp(argv[1], "--no-fork", 9) == 0){
			do_fork = 0;
			argv++;
			argc--;
			continue;
		}

		break;
	}

	// parse arg 1=target app to open
	int applen=strlen(argv[1]);
	char* application = malloc(sizeof(char)*applen);
	strcpy(application, argv[1]);

	// generate target paths: ~/.local/share/applications/app, /usr/local/share/applications/app, /usr/share/applications/app
	char* home = getenv("HOME");
	char* xdg_home=malloc(sizeof(char)*(strlen(home)+applen+strlen("/.local/share/applications/")));
	strcpy(xdg_home, home);
	xdg_home=strcat(xdg_home, "/.local/share/applications/");
	xdg_home=strcat(xdg_home, application);

	char* usr_local = malloc(sizeof(char)*(strlen("/usr/local/share/applications/")+applen));
	strcpy(usr_local, "/usr/local/share/applications/");
	usr_local = strcat(usr_local, application);

	char* usr_share = malloc(sizeof(char)*(strlen("/usr/share/applications/")+applen));
	strcpy(usr_share, "/usr/share/applications/");
	usr_share = strcat(usr_share, application);


	//Try to open paths in the following order: app, ~/.local/share/applications/app, /usr/local/share/applications/app, /usr/share/applications/app
	char* paths[]={application, xdg_home, usr_local, usr_share};

	FILE* file;

	for (int i=0; i<4; i++){
		printf("Trying to open %s\n", paths[i]);
		file = fopen(paths[i], "r");
		if (file != NULL){
			break;
		}
	}

	free(xdg_home);
	free(usr_local);
	free(usr_share);

	if (file == NULL){
		fprintf(stderr, "Could not find app %s\n", application);
		return 2;
	}

	//Parsing and validating .desktop file
	
	char* line;
	size_t size1=0;
	ssize_t size2;

	size2 = getline(&line, &size1, file);

	char dsk_ntr[17];
	strcpy(dsk_ntr, "[Desktop Entry]\n");
	if (strcmp(line, dsk_ntr) != 0){
		fprintf(stderr, "%s is not a valid application\n", application);
		return 3;
	}

	regex_t exec_regex;
	regcomp(&exec_regex, "^Exec=",0);

	regex_t term_regex;
	regcomp(&term_regex, "^Terminal=",0);

	char* exec = NULL;
	char* termline = NULL;

	while ( getline(&line, &size1, file) > 0){
		if(regexec(&exec_regex, line, 0, NULL, 0) == 0){
			if (exec != NULL) free(exec);
			exec=malloc(sizeof(char)*strlen(line));
			strcpy(exec,line);
		}
		else if(regexec(&term_regex, line, 0, NULL, 0) == 0){
			if (termline != NULL) free(termline);
			termline=malloc(sizeof(char)*strlen(line));
			strcpy(termline,line);
		}
	}

	fclose(file);
	regfree(&exec_regex);
	regfree(&exec_regex);

	if (exec == NULL){
		fprintf(stderr, "%s is not a valid application\n", application);
		return 3;
	}

	//Generating command to execute
	
	char* exec2=malloc(sizeof(char)*strlen(exec));
	strncpy(exec2, exec+5, strlen(exec)-5);

	free(exec);

	regmatch_t match[1];
	regex_t substitute;
	regcomp(&substitute, "%[FfkKuU]", 0);

	exec2=regex_sub_v(substitute,exec2,argv+2);

	regfree(&substitute);

	if (termline != NULL) {
		regex_t isterminal;
		regcomp(&isterminal, "[tT]rue", 0);
		if (regexec(&isterminal, termline, 0, NULL, 0) == 0){
			char* termname=getenv("DEFAULT_TERM");
			if (termname == NULL || strlen(termname) <= 1){
				termname="xterm -e";
			}
			char* tempexec = malloc((strlen(exec2)+strlen(termname)+1)*sizeof(char));
			memset(tempexec, '\0', sizeof(char)*strlen(tempexec));
			strcpy(tempexec, termname);
			strcat(tempexec, " ");
			strcat(tempexec, exec2);
			free(exec2);
			exec2=tempexec;
		}
	}

	//Run generated command in a forked process and concurrently return
	char* args[4]={"sh", "-c", exec2, NULL};
	if ( do_fork == 1){
		pid_t pid = fork();
		if (pid == 0) {
			if (execvp(args[0],  args) == -1) {
				printf("Something went wrong\n");
				return 1;
			}
		}
	}
	else{
		if(execvp(args[0],  args)== -1){
			printf("Something went wrong\n");
		}
	}

	free(application);
	free(exec2);
	free(termline);
	return 0;
}
